package com.demo.utils;

import com.demo.entity.CopyUser;
import com.demo.entity.Customer;
import com.demo.entity.CustomerCopy;
import com.demo.entity.User;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author wangfengchen
 * 映射工具类
 */
public class MapperUtil {
  private static final Logger logger = LoggerFactory.getLogger(
      MapperUtil.class);

  private MapperUtil() {
  }

  /**
   * 默认字段工厂
   */
  private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();

  /**
   * 默认字段实例
   */
  private static final MapperFacade MAPPER_FACADE = MAPPER_FACTORY.getMapperFacade();

  /**
   * 默认字段实例集合
   */
  private static Map<String,MapperFacade> cacheMapperFacadeMap = new ConcurrentHashMap<>();

  /**
   * 映射实体（默认字段）
   *
   * @param data    数据（对象）
   * @param toClass 映射类对象
   * @return 映射类对象
   */
  public static <E, T> E map(T data, Class<E> toClass) {
    return MAPPER_FACADE.map(data, toClass);
  }

  /**
   * 映射实体（自定义配置）
   *
   * @param data      数据（对象）
   * @param toClass   映射类对象
   * @param configMap 自定义配置
   * @return 映射类对象
   */
  public static <E, T> E map(T data, Class<E> toClass,
      Map<String,String> configMap) {
    MapperFacade mapperFacade = getMapperFacade(data.getClass(), toClass,
        configMap);
    return mapperFacade.map(data, toClass);
  }

  /**
   * 映射集合（默认字段）
   *
   * @param data    数据（集合）
   * @param toClass 映射类对象
   * @return 映射类对象
   */
  public static <E, T> List<E> mapAsList(Collection<T> data, Class<E> toClass) {
    return MAPPER_FACADE.mapAsList(data, toClass);
  }

  /**
   * 映射集合（自定义配置）
   *
   * @param data      数据（集合）
   * @param toClass   映射类
   * @param configMap 自定义配置
   * @return 映射类对象
   */
  public static <E, T> List<E> mapAsList(Collection<T> data, Class<E> toClass,
      Map<String,String> configMap) {
    T t = data.stream().findFirst()
        .orElseThrow(() -> new RuntimeException("映射集合，数据集合为空"));
    MapperFacade mapperFacade = getMapperFacade(t.getClass(), toClass,
        configMap);
    return mapperFacade.mapAsList(data, toClass);
  }

  /**
   * 获取自定义映射
   *
   * @param dataClass 数据映射类
   * @param toClass   映射类
   * @param configMap 自定义配置
   * @return 映射类对象
   */
  private static <E, T> MapperFacade getMapperFacade(Class<T> dataClass,
      Class<E> toClass, Map<String,String> configMap) {
    String mapKey =
        dataClass.getCanonicalName() + "_" + toClass.getCanonicalName();
    MapperFacade mapperFacade = cacheMapperFacadeMap.get(mapKey);
    if (Objects.isNull(mapperFacade)) {
      MapperFactory factory = new DefaultMapperFactory.Builder().build();
      ClassMapBuilder<T,E> classMapBuilder = factory.classMap(dataClass,
          toClass);
      configMap.forEach(classMapBuilder::field);
      classMapBuilder.byDefault().register();
      mapperFacade = factory.getMapperFacade();
      cacheMapperFacadeMap.put(mapKey, mapperFacade);
    }
    return mapperFacade;
  }

  /**
   * 测试
   *
   * @param args
   */
  public static void main(String[] args) {
    User user = new User();
    user.setName("user");
    user.setAge(1);
    user.setAgent("男");
    logger.info("==========【1】==========，user = {}", user);

    CopyUser copyUser = MapperUtil.map(user, CopyUser.class);
    logger.info("==========【2】==========，copyUser = {}", copyUser);

    Map<String,String> map = new HashMap<>();
    map.put("age", "copyAge");
    map.put("agent", "copyAgent");
    CopyUser copyUserMap = MapperUtil.map(user, CopyUser.class, map);
    logger.info("==========【3】==========，copyUserMap = {}", copyUserMap);

    List<User> list = new ArrayList<>();
    list.add(user);
    User user2 = new User();
    user2.setName("user2");
    user2.setAge(2);
    user2.setAgent("男2");
    list.add(user2);
    Customer customer = new Customer();
    customer.setUserList(list);
    CustomerCopy customerCopy = MapperUtil.map(customer, CustomerCopy.class);
    logger.info("==========【4】==========，customerCopy = {}", customerCopy);

    List<CopyUser> copyUserList = MapperUtil.mapAsList(customer.getUserList(),
        CopyUser.class, map);
    logger.info("==========【5】==========，customerCopyMap = {}", copyUserList);
  }

}